package com.yb.module.db.utils;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.yb.framework.core.utils.DateUtil;
import com.yb.framework.core.utils.StringUtil;
import com.yb.module.db.domain.DbColumn;
import com.yb.module.db.domain.DbTable;
import com.yb.module.system.service.SysConfigService;

/**
 * 代码生成器
 */
@Component
public class GenUtil {
	
	@Autowired
	private SysConfigService sysConfigService;
	
	private static Logger logger = LoggerFactory.getLogger(GenUtil.class.getName());
	
	public void generatorCode(DbTable table, List<DbColumn> columns, ZipOutputStream zip) {
		
		// 设置velocity资源加载器
		Properties prop = new Properties();
		// 设置velocity资源加载方式为file时的处理类
		prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		Velocity.init(prop);
		
		// 获取配置文件信息
		Configuration genConfig = getGenConfig();
		// 获取数据库/缓存配置信息
		Map<String, Object> sqlConfig = getSqlConfig();
		
		// 封装模板数据
		Map<String, Object> map = parseMap(table, columns, genConfig, sqlConfig);
		VelocityContext context = new VelocityContext(map);
		
		// 获取模板列表
		List<String> templateFileNames = getTemplateFileNames();
		for (String templateFileName : templateFileNames) {
			try {
				// 渲染模板
				StringWriter sw = new StringWriter();
				Template template = Velocity.getTemplate(templateFileName, "UTF-8");
				template.merge(context, sw);
				// 生成代码文件
				String fileName = getFileName(templateFileName, (String)map.get("className"),
						(String)map.get("prefix"), (String)map.get("suffix"));
				zip.putNextEntry(new ZipEntry(fileName));
				IOUtils.write(sw.toString(), zip, "UTF-8");
				IOUtils.closeQuietly(sw);
				zip.flush();
				zip.closeEntry();
			} catch (IOException e) {
				logger.error("渲染模板失败，表名：" + table.getTableName(), e);
			}
		}
	}
	
	/**
	 * 封装模板数据
	 */
	public Map<String, Object> parseMap(DbTable table, List<DbColumn> columns,
			Configuration genConfig, Map<String, Object> sqlConfig) {
		
		Map<String, Object> map = new HashMap<String, Object>();
		DbTable parseTable = parseTable(table, columns, genConfig, sqlConfig);

		// 导入表名
		String tableName = table.getTableName();
		map.put("tableName", tableName); //表名（如sys_user_role）
		
		// 生成的注释中，是否带“表”（默认不带“表”）
		String isTable = (String) sqlConfig.get("isTable");
		if(StringUtil.isNotEmpty(isTable) && "YES".equals(isTable.toUpperCase())) {
			map.put("tableComment", parseTable.getTableComment());
		} else {
			map.put("tableComment", parseTable.getTableComment().replace("表", ""));
		}
		// 生成的类名、对象名中，是否带前缀（默认带前缀）
		String isPrefix = (String) sqlConfig.get("isPrefix");
		if(StringUtil.isNotEmpty(isPrefix) && "NO".equals(isPrefix.toUpperCase())) {
			map.put("objName", table2ObjName(table2Suffix(tableName))); //对象名(第一个字母小写)，如：sys_user => user
			map.put("className", table2ClassName(table2Suffix(tableName))); //类名(第一个字母大写)，如：sys_user => User
			map.put("tableNameLower", tableNameLower(table2Suffix(tableName))); //表名小写（sys_user -> user）
			map.put("tableNameUpper", tableNameUpper(table2Suffix(tableName))); //表名大写（sys_user -> USER）
		} else {
			map.put("objName", table2ObjName(tableName)); //对象名(第一个字母小写)，如：sys_user => sysUser
			map.put("className", table2ClassName(tableName)); //类名(第一个字母大写)，如：sys_user => SysUser
			map.put("tableNameLower", tableNameLower(tableName)); //表名小写（sys_user -> sys_user）
			map.put("tableNameUpper", tableNameUpper(tableName)); //表名大写（sys_user -> SYS_USER）
		}
		
		// 前缀和后缀
		//前缀（sys_user_role -> sys -> system）
		//后缀（sys_user_role -> user_role）
		map.put("prefix", StringUtil.isEmpty(genConfig.getString(table2Prefix(tableName))) ?
				table2Prefix(tableName) : StringUtil.isEmpty(genConfig.getString(table2Prefix(tableName))));
		map.put("suffix", table2Suffix(tableName));
		
		// 主键和字段信息。。。
		map.put("pk", parseTable.getPk());
		map.put("columns", parseTable.getColumns());
		map.put("author", genConfig.getString("author"));
		map.put("package", genConfig.getString("package"));
		map.put("datetime", DateUtil.date2String(new Date(), DateUtil.yyyyMMddHHmmss));
		
		// 对于特殊符号处理
		map.put("xinghao", "*");
		map.put("jinghao", "#");
		
		return map;
	}
	
	/**
	 * 根据表对象、字段对象 --》 转换为类名、对象名、属性名、主键。。。
	 */
	public DbTable parseTable(DbTable table, List<DbColumn> columns,
			Configuration genConfig, Map<String, Object> sqlConfig) {
		// 表信息 --> 设置类名、对象名。。
		String tableName = table.getTableName();
		table.setObjName(table2ObjName(tableName)); //（sys_user_role --> sysUserRole）
		table.setClassName(table2ClassName(tableName)); //（sys_user_role --> SysUserRole）
		// 字段信息 --> 设置属性名、属性类型。。
		List<DbColumn> columsList = new ArrayList<DbColumn>();
		for (DbColumn column : columns) {
			// 字段名转换成Java小写属性名（user_name --> userName）
			column.setAttrName(column2AttrName(column.getColumnName()));
			// 字段名转换成Java小写属性名（user_name --> UserName）
			column.setAttributeName(column2AttributeName(column.getColumnName()));
			// 字段的数据类型转换成Java类型（若没有则默认String）
			column.setAttributeType(genConfig.getString(column.getDataType(), "String"));
			// 设置主键
			if ("PRI".equalsIgnoreCase(column.getColumnKey()) && table.getPk() == null) {
				table.setPk(column);
			} else {
				columsList.add(column);
			}
		}
		
		// 设置主键
		if (table.getPk() == null) {
			boolean flag = true;
			// 设置ID的为主键
			for (DbColumn col : table.getColumns()) {
				if ("id".equals(col.getAttrName())) {
					table.setPk(col);
					flag = false;
					break;
				}
			}
			// 若无ID字段，则设置第一个字段为主键
			if (flag) {
				table.setPk(table.getColumns().get(0));
			}
		}
		// 设置字段
		table.setColumns(columsList);
		
		return table;
	}
	
	/**
	 * 获取配置文件信息
	 */
	public Configuration getGenConfig() {
		try {
			return new PropertiesConfiguration("gen/gen.properties");
		} catch (ConfigurationException e) {
			logger.error("获取配置文件失败，", e);
			return null;
		}
	}
	
	/**
	 * 获取数据库配置信息
	 */
	public Map<String,Object> getSqlConfig() {
		Map<String,Object> sqlConfigMap = new HashMap<String, Object>();
		// 生成的注释中，是否带“表”
		sqlConfigMap.put("isTable", sysConfigService.getValueByKey("sysConfig:gen:isTable"));
		// 生成的类名、对象名中，是否带前缀
		sqlConfigMap.put("isPrefix", sysConfigService.getValueByKey("sysConfig:gen:isPrefix"));
		return sqlConfigMap;
	}
	
	/**
	 * 获取代码模板
	 */
	public List<String> getTemplateFileNames() {
		List<String> templateFileNames = new ArrayList<String>();
		templateFileNames.add("gen/templates/java/Entity.java.vm");
		templateFileNames.add("gen/templates/java/Mapper.java.vm");
		templateFileNames.add("gen/templates/java/Service.java.vm");
		templateFileNames.add("gen/templates/java/ServiceImpl.java.vm");
		templateFileNames.add("gen/templates/java/Controller.java.vm");
		templateFileNames.add("gen/templates/html/add.html.vm");
		templateFileNames.add("gen/templates/html/edit.html.vm");
		templateFileNames.add("gen/templates/html/list.html.vm");
		templateFileNames.add("gen/templates/mapper/Mapper.xml.vm");
		templateFileNames.add("gen/templates/mysql/SQL.vm");
		templateFileNames.add("gen/templates/doc/add.doc.vm");
		templateFileNames.add("gen/templates/doc/edit.doc.vm");
		templateFileNames.add("gen/templates/doc/delete.doc.vm");
		templateFileNames.add("gen/templates/doc/page.doc.vm");
		templateFileNames.add("gen/templates/doc/detail.doc.vm");
		return templateFileNames;
	}

	/**
	 * 根据模板，对生成的文件命名
	 */
	public static String getFileName(String template, String className, String prefix, String suffix) {
		if (template.contains("Entity.java.vm")) {
			return "main" + File.separator + "java" + File.separator + 
					"domain" + File.separator + className + ".java";
		} else if (template.contains("Mapper.java.vm")) {
			return "main" + File.separator + "java" + File.separator + 
					"mapper" + File.separator + className + "Mapper.java";
		} else if (template.contains("Service.java.vm")) {
			return "main" + File.separator + "java" + File.separator + 
					"service" + File.separator + className + "Service.java";
		} else if (template.contains("ServiceImpl.java.vm")) {
			return "main" + File.separator + "java" + File.separator + 
					"service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
		} else if (template.contains("Controller.java.vm")) {
			return "main" + File.separator + "java" + File.separator + 
					"controller" + File.separator + className + "Controller.java";
		} else if (template.contains("Mapper.xml.vm")) {
			return "main" + File.separator + "resources" + File.separator + 
					"mapper" + File.separator + className + "Mapper.xml";
		} else if (template.contains("list.html.vm")) {
			return "main" + File.separator + "resources" + File.separator + "pages"
					+ File.separator + prefix + File.separator + suffix + File.separator + suffix + ".html";
		} else if (template.contains("add.html.vm")) {
			return "main" + File.separator + "resources" + File.separator + "pages"
					+ File.separator + prefix + File.separator + suffix + File.separator + suffix + "_add.html";
		} else if (template.contains("edit.html.vm")) {
			return "main" + File.separator + "resources" + File.separator + "pages"
					+ File.separator + prefix + File.separator + suffix + File.separator + suffix + "_edit.html";
		} else if (template.contains("SQL.vm")) {
			return "sql" + File.separator + className + ".sql";
		} else if (template.contains("add.doc.vm")) {
			return "doc" + File.separator + className + "_add.txt";
		} else if (template.contains("edit.doc.vm")) {
			return "doc" + File.separator + className + "_edit.txt";
		} else if (template.contains("delete.doc.vm")) {
			return "doc" + File.separator + className + "_delete.txt";
		} else if (template.contains("page.doc.vm")) {
			return "doc" + File.separator + className + "_page.txt";
		} else if (template.contains("detail.doc.vm")) {
			return "doc" + File.separator + className + "_detail.txt";
		}
		return null;
	}
	
	/**
	 * 转换字符串第一个字符为小写
	 */
	public static String getStrByLowerFirstChar(String str) {
		try {
			return str.substring(0, 1).toLowerCase() + str.substring(1);
		} catch (Exception e) {
			return "";
		}
	}
	
	/**
	 * 转换字符串第一个字符为大写
	 */
	public static String getStrByUpperFirstChar(String str) {
		try {
			return str.substring(0, 1).toUpperCase() + str.substring(1);
		} catch (Exception e) {
			return "";
		}
	}
	
	// 表名小写（sys_user_role --> sys_user_role）
	public static String tableNameLower(String tableName) {
		return tableName.toLowerCase();
	}
	
	// 表名大写（sys_user_role --> SYS_USER_ROLE）
	public static String tableNameUpper(String tableName) {
		return tableName.toUpperCase();
	}
	
	// 表名提取前缀（sys_user_role --> sys）
	public static String table2Prefix(String tableName) {
		if(StringUtil.isEmpty(tableName)) {
			return null;
		}
		String array[] = tableName.split("_");
		String prefix = array[0];
		return prefix;
	}
	
	// 表名提取后缀（sys_user_role --> user_role）
	public static String table2Suffix(String tableName) {
		if(StringUtil.isEmpty(tableName)) {
			return null;
		}
		String array[] = tableName.split("_");
		String suffix = array[1];
		if(array.length >= 2) {
			for(int i=2; i<array.length; i++) {
				suffix = suffix + "_" + array[i];
			}
		}
		return suffix;
	}
	
	// 表名提取对象名（sys_user_role --> sysUserRole）
	public static String table2ObjName(String tableName) {
		if(StringUtil.isEmpty(tableName)) {
			return null;
		}
		String array[] = tableName.split("_");
		String objName = array[0];
		if(array.length >= 1) {
			for(int i=1; i<array.length; i++) {
				objName = objName + getStrByUpperFirstChar(array[i]);
			}
		}
		return objName;
	}
	
	// 表名提取类名（sys_user_role --> SysUserRole）
	public static String table2ClassName(String tableName) {
		if(StringUtil.isEmpty(tableName)) {
			return null;
		}
		String array[] = tableName.split("_");
		String className = getStrByUpperFirstChar(array[0]);
		if(array.length >= 1) {
			for(int i=1; i<array.length; i++) {
				className = className + getStrByUpperFirstChar(array[i]);
			}
		}
		return className;
	}
	
	// 字段名转换成小写属性名（user_name --> userName）
	public static String column2AttrName(String columnName) {
		if(StringUtil.isEmpty(columnName)) {
			return null;
		}
		String array[] = columnName.split("_");
		String attrName = array[0];
		for(int i=1; i<array.length; i++) {
			attrName = attrName + getStrByUpperFirstChar(array[i]);
		}
		return attrName;
	}
	
	// 字段名转换成大写属性名（user_name --> UserName）
	public static String column2AttributeName(String columnName) {
		if(StringUtil.isEmpty(columnName)) {
			return null;
		}
		String array[] = columnName.split("_");
		String attributeName = getStrByUpperFirstChar(array[0]);
		for(int i=1; i<array.length; i++) {
			attributeName = attributeName + getStrByUpperFirstChar(array[i]);
		}
		return attributeName;
	}
}
